/**
 * 汇编指令的封装
 * 作者：lcz
 */
#ifndef CPU_INSTR_H
#define CPU_INSTR_H

#include "types.h"
#include "cpu/irq.h"

/**
 * @brief define gdt struct,include attribute segment limit and segment base address
 *
 */
typedef struct gdt
{
    uint16_t limit;
    uint16_t base0_15;
    uint16_t base16_31;
} gdt_struct;

typedef struct idt
{
    uint16_t limit;
    uint16_t base0_15;
    uint16_t base16_31;
} idt_struct;

/**
 * @brief close interrupt
 *
 */
static inline void
cli()
{
    __asm__ __volatile__(
        "cli");
}
/**
 * @brief setup interrupt
 *
 */
static inline void sti()
{
    __asm__ __volatile__(
        "sti");
}

/**
 * @brief read port value
 *
 * @param port :  read data orm this port
 * @return uint8_t : result of read
 */
static inline uint8_t inb(uint16_t port)
{
    // inb al.dx
    uint8_t return_value;
    __asm__ __volatile__(
        "inb %[p],%[v]"
        : [v] "=a"(return_value)
        : [p] "d"(port));
}

/**
 * @brief read port value(word,two bytes)
 *
 * @param port :   read data orm this port
 * @return uint16_t: result of read
 */
static inline uint16_t inw(uint16_t port)
{
    // in al.dx
    uint16_t return_value;
    __asm__ __volatile__(
        "in %[p],%[v]"
        : [v] "=a"(return_value)
        : [p] "d"(port));
}

/**
 * @brief output data to specified port
 *
 * @param port :  port of output
 * @param data : data of output
 */
static inline void outb(uint16_t port, uint8_t data)
{
    __asm__ __volatile__(
        "outb %[v],%[p]" ::[p] "d"(port), [v] "a"(data));
}

/**
 * @brief output data to specified port
 *
 * @param port :  port of output
 * @param data : data of output
 */
static inline void outw(uint16_t port, uint16_t data)
{
    __asm__ __volatile__(
        "out %[v],%[p]" ::[p] "d"(port), [v] "a"(data));
}

/**
 * @brief   use lgdt instruction load gdt table
 *
 * @param start : segment base address
 * @param size : segment size(limit)
 */
static inline void lgdt(uint32_t start, uint32_t size)
{
    gdt_struct gdt;
    gdt.limit = size - 1;
    gdt.base0_15 = start & 0xFFFF;
    gdt.base16_31 = start >> 16;

    __asm__ __volatile__(
        "lgdt %[g]" ::[g] "m"(gdt));
}

static inline void lidt(uint32_t start, uint32_t size)
{
    idt_struct idt;
    idt.limit = size - 1;
    idt.base0_15 = start & 0xFFFF;
    idt.base16_31 = start >> 16;

    __asm__ __volatile__(
        "lidt %[g]" ::[g] "m"(idt));
}

static inline uint32_t read_cr0(void)
{
    uint32_t cr0;
    __asm__ __volatile__(
        "mov %%cr0 , %[v]"
        : [v] "=r"(cr0));
    return cr0;
}

static inline void write_cr0(uint32_t value)
{
    __asm__ __volatile__(
        "mov %[v],%%cr0" ::[v] "r"(value));
}

static inline uint32_t read_cr2(void)
{
    uint32_t cr2;
    __asm__ __volatile__(
        "mov %%cr2 , %[v]"
        : [v] "=r"(cr2));
    return cr2;
}

static inline void write_cr2(uint32_t value)
{
    __asm__ __volatile__(
        "mov %[v],%%cr2" ::[v] "r"(value));
}

static inline uint32_t read_cr3(void)
{
    uint32_t cr3;
    __asm__ __volatile__(
        "mov %%cr3 , %[v]"
        : [v] "=r"(cr3));
    return cr3;
}

static inline void write_cr3(uint32_t value)
{
    __asm__ __volatile__(
        "mov %[v],%%cr3" ::[v] "r"(value));
}

static inline uint32_t read_cr4(void)
{
    uint32_t cr4;
    __asm__ __volatile__(
        "mov %%cr4 , %[v]"
        : [v] "=r"(cr4));
    return cr4;
}

static inline void write_cr4(uint32_t value)
{
    __asm__ __volatile__(
        "mov %[v],%%cr4" ::[v] "r"(value));
}

// note!!here selector(segment selector is only 16 bits)
// but you want to let the var addr is array,so must 32 bits
// when use only use low 16 bits
// switch process is user far_jump,use ljmpl instruction Exactly
static inline void far_jump(uint32_t selector, uint32_t offset)
{
    uint32_t addr[] = {
        offset,
        selector,
    };
    __asm__ __volatile__(
        "ljmpl *(%[a])" ::[a] "r"(addr));
}

// stop 
static inline void hlt(void){
    __asm__ __volatile__("hlt");
}

/**
 * @brief load the task(tss) register
 * 
 * @param tss_sel  segment selector
 */
static inline void write_tr(uint16_t tss_sel){
    __asm__ __volatile__("ltr %%ax" ::"a"(tss_sel));
}

// 读写状态寄存器
static inline irq_state_t read_eflags(void)
{
    irq_state_t eflags;
    __asm__ __volatile__("pushf \n\tpop %%eax"
                         :"=a"(eflags));
    return eflags;
}

static inline void write_eflags(irq_state_t eflags)
{
    __asm__ __volatile__("push %%eax\n\tpopf" ::"a"(eflags));
}

#endif
